

const audioEl = document.querySelector('audio');
const cvs = document.querySelector('canvas');

const ctx = cvs.getContext('2d');

// 初始化canva尺寸
function initCvs(params) {
  cvs.width = window.innerWidth * devicePixelRatio
  cvs.height = window.innerHeight / 2 * devicePixelRatio
}

initCvs()

let isInit = false, dataArray, analyser;

audioEl.onplay = () => {
  if (isInit) {
    return
  }
  // 初始化
  // 创建音频上下文，包含音频处理的各个节点
  const audioCtx = new AudioContext();
  // 创建音频节点(比如修音、音效、混响、调节音调等都是一个节点)
  const source = audioCtx.createMediaElementSource(audioEl);
  // 创建分析节点
  analyser = audioCtx.createAnalyser();
  // 设置分析节点的FFT大小（快速傅立叶变换进行频谱分析）值越大分析的频率越准确，但是性能消耗越大
  analyser.fftSize = 512;
  // 创建一个Uint8Array（无符号8位数组，一个字节）类型的数组，用于存放分析结果
  const bufferLength = analyser.frequencyBinCount; // 512 / 2 快速傅立叶变换分析之后的数据是对称的，所以取一半就可以了
  dataArray = new Uint8Array(bufferLength);

  // 将音频节点连接到分析节点
  source.connect(analyser);
  // 将分析节点连接到音频输出设备
  analyser.connect(audioCtx.destination);
  isInit = true
}

// 绘制
function draw() {
  requestAnimationFrame(draw)
  // 清空画布
  ctx.clearRect(0, 0, cvs.width, cvs.height);
  if (!isInit) {
    return
  }
  // 获取分析结果
  analyser.getByteFrequencyData(dataArray);
  // console.log(dataArray)
  // 音乐的频谱中有部分频率是高频的，人耳听不到的，所以只取前一半
  const len = dataArray.length / 2.5;
  // 柱状图宽度 = 画布宽度 / 频谱长度
  // 柱状图宽度 = 柱状图宽度 / 2 （因为经过傅立叶变换，柱状图是左右对称的，我们绘制的是右边的，所以宽度要除以2（图的条数增加了，宽度要减半））
  const barWidth = cvs.width / len / 2;
  ctx.fillStyle = '#78c5f7'
  // 绘制
  for (let i = 0; i < len; i++) {
    const data = dataArray[i]; // < 256(一个字节大小)
    const barHeight = data / 255 * cvs.height;
    // 柱状图绘制位置 右边一半
    const x1 = i * barWidth + cvs.width / 2
    // 柱状图绘制位置 左边一半
    const x2 = cvs.width / 2 - (i + 1) * barWidth
    const y = cvs.height - barHeight
    ctx.fillRect(x1, y, barWidth - 3, barHeight);
    ctx.fillRect(x2, y, barWidth - 3, barHeight);
  }
}

draw()
